﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using JasLib.Extensions;

namespace JasLib.Interop.Win32
{
    public static partial class Kernel32Helper
    {
        /************************************************************************************/
        /// <summary>
        /// From "Advanced Windows" by Jeffrey Richter, page 750.
        /// </summary>
        /// <param name="hCompletionPort"></param>
        /// <param name="hDevice"></param>
        /// <param name="dwCompKey"></param>
        /// <returns></returns>
        public static bool AssociateDeviceWithCompletionPort(HANDLE hCompletionPort, HANDLE hDevice, UIntPtr dwCompKey)
        {
            HANDLE h = KERNEL32.CreateIoCompletionPort(hDevice, hCompletionPort, dwCompKey, 0);
            return (h == hCompletionPort);
        }

        /************************************************************************************/
        /// <summary>
        /// From "Advanced Windows" by Jeffrey Richter, page 747.
        /// </summary>
        /// <param name="NumberOfConcurrentThreads"></param>
        /// <returns></returns>
        public static HANDLE CreateNewCompletionPort(uint NumberOfConcurrentThreads)
        {
            return KERNEL32.CreateIoCompletionPort(HANDLE.INVALID_HANDLE_VALUE, HANDLE.NULL, UIntPtr.Zero, NumberOfConcurrentThreads);
        }

        /************************************************************************************/
        public static IEnumerable<WIN32_FIND_DATA> EnumFiles(string strPath)
        {
            HANDLE hSearch = HANDLE.NULL;

            try
            {
                WIN32_FIND_DATA fileData;

                hSearch = KERNEL32.FindFirstFile(strPath, out fileData);
                if (!hSearch.IsNullOrInvalid)
                {
                    do
                    {
                        yield return fileData;
                    }
                    while (KERNEL32.FindNextFile(hSearch, out fileData));
                }

                if (!KERNEL32.GetLastError().In(Win32ErrorCode.ERROR_FILE_NOT_FOUND, Win32ErrorCode.ERROR_NO_MORE_FILES))
                    throw new Win32Exception();
            }
            finally
            {
                if (!hSearch.IsNullOrInvalid)
                    KERNEL32.FindClose(hSearch);
            }
        }

        /************************************************************************************/
        public static IEnumerable<WIN32_FIND_STREAM_DATA> EnumStreams(string strFilePath)
        {
            HANDLE hSearch = HANDLE.NULL;

            try
            {
                WIN32_FIND_STREAM_DATA streamData;

                hSearch = KERNEL32.FindFirstStreamW(strFilePath, STREAM_INFO_LEVELS.FindStreamInfoStandard, out streamData, 0);
                if (!hSearch.IsNullOrInvalid)
                {
                    do
                    {
                        yield return streamData;
                    }
                    while (KERNEL32.FindNextStreamW(hSearch, out streamData));
                }

                if (!KERNEL32.GetLastError().In(Win32ErrorCode.ERROR_HANDLE_EOF))
                    throw new Win32Exception();
            }
            finally
            {
                if (!hSearch.IsNullOrInvalid)
                    KERNEL32.FindClose(hSearch);
            }
        }

        /************************************************************************************/
        public static IEnumerable<THREADENTRY32> EnumProcessThreads(uint uiProcessID)
        {
            HANDLE hSnapshot = HANDLE.NULL;

            try
            {
                hSnapshot = KERNEL32.CreateToolhelp32Snapshot(SnapshotFlags.TH32CS_SNAPTHREAD, uiProcessID);
                if (hSnapshot.IsNullOrInvalid)
                    yield break;

                /*const*/ uint uiStructSize = MarshalHelper.SizeOf<THREADENTRY32>();

                THREADENTRY32 threadInfo = new THREADENTRY32
                {
                    dwSize = uiStructSize,
                };
                if (!KERNEL32.Thread32First(hSnapshot, ref threadInfo))
                    yield break;

                do
                {
                    if (threadInfo.th32OwnerProcessID == uiProcessID)
                        yield return threadInfo;

                    threadInfo = new THREADENTRY32
                    {
                        dwSize = uiStructSize,
                    };
                }
                while (KERNEL32.Thread32Next(hSnapshot, ref threadInfo));
            }
            finally
            {
                if (!hSnapshot.IsNullOrInvalid)
                    KERNEL32.CloseHandle(hSnapshot);
            }
        }

        /************************************************************************************/
        public static IEnumerable<MODULEENTRY32> EnumProcessModules(uint uiProcessId)
        {
            HANDLE hSnapshot = HANDLE.NULL;

            try
            {
                hSnapshot = KERNEL32.CreateToolhelp32Snapshot(SnapshotFlags.TH32CS_SNAPMODULE, uiProcessId);
                if (hSnapshot.IsNullOrInvalid)
                    yield break;

                /*const*/ uint uiStructSize = MarshalHelper.SizeOf<MODULEENTRY32>();

                MODULEENTRY32 moduleInfo = new MODULEENTRY32
                {
                    dwSize = uiStructSize,
                };
                if (!KERNEL32.Module32First(hSnapshot, ref moduleInfo))
                    yield break;

                do
                {
                    yield return moduleInfo;

                    moduleInfo = new MODULEENTRY32
                    {
                        dwSize = uiStructSize,
                    };
                }
                while (KERNEL32.Module32Next(hSnapshot, ref moduleInfo));
            }
            finally
            {
                if (!hSnapshot.IsNullOrInvalid)
                    KERNEL32.CloseHandle(hSnapshot);
            }
        }

        /************************************************************************************/
        public static HANDLE OpenConsoleOutputHandle()
        {
            /// These parameters are not the easiest to remember but they must be exact.
            /// http://support.microsoft.com/kb/9008
            SECURITY_ATTRIBUTES securityAttributes = new SECURITY_ATTRIBUTES();
            return KERNEL32.CreateFile("CONOUT$", FileAccessRights.GENERIC_WRITE, FileShareMode.FILE_SHARE_WRITE, ref securityAttributes, FileCreationDisposition.OpenExisting, FileFlagsAndAttributes.FILE_ATTRIBUTE_NORMAL, HANDLE.NULL);
        }
    }
}
